﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
// std headers

// qt headers
#include <QLabel>
#include <QTimer>
#include <QHBoxLayout>
#include <QGraphicsDropShadowEffect>
#include <QGraphicsOpacityEffect>
#include <QPropertyAnimation>
#include <QPixmap>
#include <QPainter>
#include <QFontMetrics>
#include <QEvent>
#include <QPainter>

// gcmp headers

// custom headers
#include "BnufBulletin.h"
#include "GbmpNew.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h" 

namespace gmepgcs
{
    BnufBulletin::BnufBulletin(QWidget *parent) :
        QWidget(parent),
        m_pLblIcon(nullptr),
        m_pLblText(nullptr),
        m_pLblProgress(nullptr),
        m_textFont(),
        m_progressTotal(0),
        m_progressCurrent(0)
    {
        Init();
    }

    BnufBulletin::~BnufBulletin()
    {

    }

    bool BnufBulletin::eventFilter(QObject * watched, QEvent * event)
    {
        if (!watched || !event)
        {
            return QWidget::eventFilter(watched, event);
        }
        DBG_WARN_AND_RETURN_FALSE_UNLESS(m_pLblProgress, L"m_pLblProgress为空", L"GMEP", L"2024-01-30");        
        if (watched == m_pLblProgress && event->type() == QEvent::Type::Paint)
        {
            QPainter painter(m_pLblProgress);

            // draw progress bar - base
            QRect rect = m_pLblProgress->rect();
            QSize barSize = QSize(m_pLblProgress->width(), 12);
            QPoint barTopLeft((int)(rect.width() - barSize.width()) / 2, (int)(rect.height() - barSize.height()) / 2);
            QRect barRect(rect.topLeft() + barTopLeft, barSize);
            auto radius = barSize.height() / 2.0;

            QPainterPath barBasePath;
            barBasePath.moveTo(barRect.topLeft() + QPointF(radius, 0));
            barBasePath.lineTo(barRect.topRight() + QPointF(-radius, 0));
            barBasePath.arcTo(QRectF(barRect.topRight() + QPointF(-radius * 2, 0), barRect.bottomRight()), 90, -180);
            barBasePath.lineTo(barRect.bottomLeft() + QPointF(radius, 0));
            barBasePath.arcTo(QRectF(barRect.topLeft(), barRect.bottomLeft() + QPointF(radius * 2, 0)), 90, 180);
            QColor &barBaseColor = QColor(245, 245, 245);
            painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing);
            painter.fillPath(barBasePath, QBrush(barBaseColor, Qt::BrushStyle::SolidPattern));

            // draw progress bar - front
            float progress = m_progressCurrent / ((float)(m_progressTotal));
            int progressLength = (int)progress * barSize.width();
            if (progressLength >= radius)
            {
                QRect barFrontRect = barRect.adjusted(0, 0, -(barRect.width() - progressLength), 0);
                QPainterPath barFrontPath;
                barFrontPath.moveTo(barFrontRect.topLeft() + QPointF(radius, 0));
                barFrontPath.lineTo(barFrontRect.topRight() + QPointF(-radius, 0));
                barFrontPath.arcTo(QRectF(barFrontRect.topRight() + QPointF(-radius * 2, 0), barFrontRect.bottomRight()), 90, -180);
                barFrontPath.lineTo(barFrontRect.bottomLeft() + QPointF(radius, 0));
                barFrontPath.arcTo(QRectF(barFrontRect.topLeft(), barFrontRect.bottomLeft() + QPointF(radius * 2, 0)), 90, 180);
                QColor &barFrontColor = QColor(26, 144, 255, 255);
                painter.setRenderHint(QPainter::RenderHint::HighQualityAntialiasing);
                painter.fillPath(barFrontPath, QBrush(barFrontColor, Qt::BrushStyle::SolidPattern));
            }

            painter.restore();

            event->accept();
            return true;
        }

        return QWidget::eventFilter(watched, event);
    }
    
    QWidget *BnufBulletin::s_pWgtDock = nullptr;

    BnufBulletin * BnufBulletin::Create(QWidget * parent)
    {
        if (parent)
        {
            return NEW_AS_QT(BnufBulletin,parent);
        }

        if (BnufBulletin::s_pWgtDock)
        {
            return NEW_AS_QT(BnufBulletin,BnufBulletin::s_pWgtDock);
        }

        return nullptr;
    }

    void BnufBulletin::SingleShot(const std::wstring & message, const BulletinIcon & icon, const int32_t &duration, QWidget * parent)
    {
        BnufBulletin* bulletin = BnufBulletin::Create(parent);
        if (!bulletin)
        {
            return;
        }
        DBG_WARN_AND_RETURN_VOID_UNLESS(bulletin->ConfigIcon(icon), L"bulletin->ConfigIcon(icon)为空", L"GMEP", L"2024-01-30");
        DBG_WARN_AND_RETURN_VOID_UNLESS(bulletin->ConfigIcon(icon)->ConfigMessage(message), L"bulletin->ConfigIcon(icon)->ConfigMessage(message)为空", L"GMEP", L"2024-01-30");
        bulletin->ConfigIcon(icon)->ConfigMessage(message)->show();
        
        QTimer::singleShot(duration < 0 ? 0 : duration, [=] {bulletin->StartExit(); });
    }

    void BnufBulletin::SetDock(QWidget * dock)
    {
        BnufBulletin::s_pWgtDock = dock;
    }

    void BnufBulletin::paintEvent(QPaintEvent * event)
    {
        QPainter painter(this);
        painter.setPen(Qt::NoPen);
        painter.setBrush(Qt::white);
        painter.drawRoundedRect(rect(), 5, 5);
    }

    void BnufBulletin::closeEvent(QCloseEvent * event)
    {
        emit WillClose();

        QWidget::closeEvent(event);
    }

    void BnufBulletin::Init()
    {
        // icon
        m_pLblIcon = NEW_AS_QT_CHILD(QLabel,this);
        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pLblIcon, L"m_pLblIcon为空", L"GMEP", L"2024-01-30");
        m_pLblIcon->setFixedSize(QSize(16, 16));
        m_pLblIcon->setScaledContents(true);

        // text
        m_pLblText = NEW_AS_QT_CHILD(QLabel,this);
        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pLblText, L"m_pLblText为空", L"GMEP", L"2024-01-30");
        m_textFont = m_pLblText->font();
        m_textFont.setFamily("Microsoft YaHei");
        m_textFont.setPixelSize(12);
        m_pLblText->setFont(m_textFont);
        m_pLblText->setStyleSheet(
            QString(
                "QLabel{"
                "color:rgba(40, 40, 51, 255);"
                "}"
            )
        );

        // progress
        m_pLblProgress = NEW_AS_QT_CHILD(QLabel,this);
        DBG_WARN_AND_RETURN_VOID_UNLESS(m_pLblProgress, L"m_pLblProgress为空", L"GMEP", L"2024-01-30");
        m_pLblProgress->installEventFilter(this);
        m_pLblProgress->setVisible(false);
        m_pLblProgress->setFixedWidth(160);
        m_pLblProgress->setMinimumHeight(12);

        // layout 
        QHBoxLayout *pLayout = NEW_AS_QT_CHILD(QHBoxLayout, this);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pLayout, L"pLayout为空", L"GMEP", L"2024-01-30");
        pLayout->setContentsMargins(16, 8, 16, 8);
        pLayout->setSpacing(8);
        pLayout->addStretch();
        pLayout->addWidget(m_pLblIcon);
        pLayout->setAlignment(m_pLblIcon, Qt::AlignVCenter | Qt::AlignHCenter);
        pLayout->addWidget(m_pLblText);
        pLayout->setAlignment(m_pLblText, Qt::AlignVCenter | Qt::AlignHCenter);
        pLayout->addWidget(m_pLblProgress);
        pLayout->setAlignment(m_pLblProgress, Qt::AlignVCenter | Qt::AlignHCenter);
        pLayout->addStretch();

        // self
        setLayout(pLayout);
        setWindowFlags(Qt::Widget | Qt::WindowCloseButtonHint | Qt::WindowStaysOnTopHint);
        setMinimumWidth(128);
        setMinimumHeight(40);
        setStyleSheet(
            "QWidget#ShadowWidget{"
            "background-color:#00FFFF;"
            "border-radius:100px;"
            "}"
        );

        // set shadow
        setAttribute(Qt::WA_DeleteOnClose);
        QGraphicsDropShadowEffect *pShadowEffect = NEW_AS_QT(QGraphicsDropShadowEffect,this);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pShadowEffect, L"pShadowEffect为空", L"GMEP", L"2024-01-30");
        pShadowEffect->setOffset(1, 1);
        pShadowEffect->setColor(QColor("#dbdbdb"));
        pShadowEffect->setBlurRadius(5);
        setGraphicsEffect(pShadowEffect);
    }

    BnufBulletin * BnufBulletin::ConfigMessage(const std::wstring & message)
    {
        DBG_WARN_AND_RETURN_UNLESS(m_pLblText, this, L"m_pLblText 为空", L"GMEP", L"2024-01-30");
        m_pLblText->setText(QString::fromStdWString(message));
        ConfigGeometry();
        return this;
    }

    BnufBulletin * BnufBulletin::ConfigIcon(const BulletinIcon & icon)
    {
        QString iconPath;
        switch (icon)
        {
        case BulletinIcon::Info:
            iconPath = ":/images/Announcement/Info/16x16.png";
            break;
        case BulletinIcon::Warning:
            iconPath = ":/images/Announcement/HighWarning/16x16.png";
            break;
        case BulletinIcon::Error:
            iconPath = ":/images/Announcement/Error/16x16.png";
            break;
        case BulletinIcon::Success:
            iconPath = ":/images/Announcement/Success/16x16.png";
            break;
        case BulletinIcon::Close:
            iconPath = ":/images/Announcement/CloseNoBackGround/16x16.png";
            break;
        case BulletinIcon::Waiting:
            iconPath = ":/images/Announcement/Waiting/16x16.png";
            break;
        default:
            break;
        }

        DBG_WARN_AND_RETURN_UNLESS(m_pLblIcon, this, L"m_pLblIcon 为空", L"GMEP", L"2024-01-30");
        if (!iconPath.isEmpty())
        {
            QPixmap iconPixmap;
            iconPixmap.load(iconPath);
            m_pLblIcon->setPixmap(iconPixmap.scaled(m_pLblIcon->size(), Qt::KeepAspectRatio, Qt::TransformationMode::SmoothTransformation));
            m_pLblIcon->setVisible(true);
        }
        else
        {
            m_pLblIcon->clear();
            m_pLblIcon->setVisible(false);
        }
        
        return this;
    }

    BnufBulletin * BnufBulletin::ConfigProgress(const int & current, const int & total)
    {
        if (m_progressCurrent == current && m_progressTotal == total)
        {
            return this;
        }

        m_progressCurrent = current;
        m_progressTotal = total;
        DBG_WARN_AND_RETURN_UNLESS(m_pLblProgress, this, L"m_pLblProgress 为空", L"GMEP", L"2024-01-30");
        m_pLblProgress->setVisible(m_progressTotal > 0);
        m_pLblProgress->update();
        ConfigGeometry();

        return this;
    }

    void BnufBulletin::CloseLater(const int32_t & delay)
    {
        QTimer::singleShot(delay < 0 ? 0 : delay, [=] {StartExit(); });
    }

    BnufBulletin * BnufBulletin::ConfigGeometry()
    {
        adjustSize();

        auto parent = parentWidget();
        if (parent != nullptr)
        {
            int left = (parent->width() - width()) / 2;
            int top = 56;
            move(left, top);
        }

        return this;
    }

    void BnufBulletin::StartExit()
    {
        QGraphicsOpacityEffect *pOpacityEffect = NEW_AS_QT(QGraphicsOpacityEffect,this);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pOpacityEffect, L"pOpacityEffect为空", L"GMEP", L"2024-01-30");
        setGraphicsEffect(pOpacityEffect);

        QPropertyAnimation *pExitAnimation = NEW_AS_QT(QPropertyAnimation,pOpacityEffect);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pExitAnimation, L"pExitAnimation为空", L"GMEP", L"2024-01-30");
        pExitAnimation->setTargetObject(this);
        pExitAnimation->setPropertyName("opacity");
        pExitAnimation->setDuration(500);
        pExitAnimation->setStartValue(1);
        pExitAnimation->setEndValue(0);
        connect(pExitAnimation, &QPropertyAnimation::finished, [=]() {
            close();
        });
        pExitAnimation->start();
    }
}